﻿using CpuSimulator.Instructions;
using System;
using System.Collections.Generic;
using System.Text;

namespace CpuSimulator
{
    public enum InstructionTypeEnum {         
        R,//Register的缩写，操作两个寄存器，并把结果写到第三个寄存器上的类型
        I,//Immediate的缩写，操作一个寄存器和一个常量，并把结果写到第二个寄存器上的类型
        S,//Store的缩写，用于将寄存器数据写入内存，操作两个寄存器和一个常量
        B,//Branch的缩写，用于分支判断，操作两个寄存器和一个常量
        U,//Upper的缩写，用于取一个数的高20位，将一个常量的高20位写到一个寄存器里
        J,//Jump的缩写，用于跳转，操作一个寄存器和一个常量
    }

    public enum OpCodeEnum
    {
        LOAD =           0b0000011,
        LOAD_FP=       0b0000111,
        CUSTOM0=     0b0001011,
        MISC_MEM=   0b0001111,
        OP_IMM=       0b0010011,
        AUPIC=           0b0010111,
        OP_IMM32=   0b0011011,
        STORE=          0b0100011,
        STORE_FP=     0b0100111,
        CUSTOM1=    0b0101011,
        AMO=            0b0101111,
        OP=               0b0110011,
        LUI=               0b0110111,
        OP32=           0b0111011,
        MADD=         0b1000011,
        MSUB=          0b1000111,
        NMADD=      0b1001111,
        OP_FP=          0b1010011,
        BRANCH=      0b1100011,
        JALR=             0b1100111,
        JAL=               0b1101111,
        SYSTEM=       0b1110011
    }
    public class Instruction
    {
        public InstructionTypeEnum InsType;//指令类型
        public OpCodeEnum OpCode;//OpCode
        public short Rd;//目标寄存器编号
        public short Rs1;//第一个源寄存器编号
        public short Rs2;//第二个源寄存器编号
        public int Immediate;//立即数，常量
        public short Fun3;//三个字节的编码
        public short Fun7;//七个字节的编码
        public string ascii="";
        public static Instruction? Parse(uint data)
        {            
            Instruction ins = new Instruction();
            ins.ascii += (char)(data & 0xff);
            ins.ascii += (char)((data >> 8) & 0xff);
            ins.ascii += (char)((data >> 16) & 0xff);
            ins.ascii += (char)(data >> 24);
            ins.OpCode = (OpCodeEnum)(data & 0x7f);
            ins.Rd = (short)((data >> 7) & 0x1f);
            ins.Rs1 = (short)((data >> 15) & 0x1f);
            ins.Rs2 = (short)((data >> 20) & 0x1f);
            ins.Fun3 = (short)((data >> 12) & 0x7);
            ins.Fun7 = (short)((data >> 25) & 0x7f);
            switch (ins.OpCode)
            {
                case OpCodeEnum.OP:
                    ins.InsType = InstructionTypeEnum.R;
                    break;
                case OpCodeEnum.LOAD:
                case OpCodeEnum.OP_IMM:
                case OpCodeEnum.JALR:
                    ins.InsType = InstructionTypeEnum.I;
                    ins.Immediate = ((int)data) >> 20;
                    break;
                case OpCodeEnum.BRANCH:
                    ins.InsType = InstructionTypeEnum.B;
                    ins.Immediate = (int)((((((int)data >> 20) & 0xFFFFFFE0) | ((data >> 7) & 0x0000001F)) &0xFFFFF7FE) | ((((((int)data >> 20) &0xFFFFFFE0) | ((data >> 7) &0x0000001F))  &0x00000001) << 11));
                    break;
                case OpCodeEnum.STORE:
                    ins.InsType = InstructionTypeEnum.S;
                    ins.Immediate = (int)((((int)data >> 20) & 0xFFFFFFE0) | ((data >> 7)  &0x0000001F));
                    break;
                case OpCodeEnum.LUI:
                case OpCodeEnum.AUPIC:
                    ins.InsType = InstructionTypeEnum.U;
                    ins.Immediate = (int)(data >> 12);
                    break;
                case OpCodeEnum.JAL:
                    ins.InsType = InstructionTypeEnum.J;
                    ins.Immediate = (int)((((int)data >> 20) &0xFFF007FE) | ((data >> 9) &0x00000800) | (data &0x000FF000));
                    break;
                default:
                    break;
            }
            Instruction rv = new Instruction();
            switch (ins)
            {
                #region I_Type 15个指令
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b000 }:
                    rv = new ADDI();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b111 }:
                    rv = new ANDI();
                    break;
                case { OpCode: OpCodeEnum.JALR, Fun3: 0b000 }:
                    rv = new JALR();
                    break;
                case { OpCode: OpCodeEnum.LOAD, Fun3: 0b000 }:
                    rv = new LB();
                    break;
                case { OpCode: OpCodeEnum.LOAD, Fun3: 0b100 }:
                    rv = new LBU();
                    break;
                case { OpCode: OpCodeEnum.LOAD, Fun3: 0b001 }:
                    rv = new LH();
                    break;
                case { OpCode: OpCodeEnum.LOAD, Fun3: 0b101 }:
                    rv = new LHU();
                    break;
                case { OpCode: OpCodeEnum.LOAD, Fun3: 0b010 }:
                    rv = new LW();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b110 }:
                    rv = new ORI();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b001 }:
                    rv = new SLLI();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b010 }:
                    rv = new SLTI();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b011 }:
                    rv = new SLTIU();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b101 }:
                    if(ins.Fun7 == 0b0000000)
                        rv = new SRLI();
                    if(ins.Fun7 == 0b0100000)
                        rv = new SRAI();
                    break;
                case { OpCode: OpCodeEnum.OP_IMM, Fun3: 0b100 }:
                    rv = new XORI();
                    break;
                #endregion

                #region R_Type 10个指令
                case { OpCode: OpCodeEnum.OP, Fun3: 0b000 }:
                    if (ins.Fun7 == 0b0000000)
                        rv = new ADD();
                    if (ins.Fun7 == 0b0100000)
                        rv = new SUB();
                    if (ins.Fun7 == 0b0000001)
                        rv = new MUL();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b111 }:
                    rv = new AND();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b110 }:
                    rv = new OR();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b001 }:
                    rv = new SLL();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b010 }:
                    rv = new SLT();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b011 }:
                    rv = new SLTU();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b101 }:
                    if(ins.Fun7 == 0b0000000)
                        rv = new SRL();
                    if (ins.Fun7 == 0b0100000)
                        rv = new SRA();
                    break;
                case { OpCode: OpCodeEnum.OP, Fun3: 0b100 }:
                    rv = new XOR();
                    break;
                #endregion

                #region B_Type 6个指令
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b000 }:
                    rv = new BEQ();
                    break;
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b001 }:
                    rv = new BNE();
                    break;
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b100 }:
                    rv = new BLT();
                    break;
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b101 }:
                    rv = new BGE();
                    break;
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b110 }:
                    rv = new BLTU();
                    break;
                case { OpCode: OpCodeEnum.BRANCH, Fun3: 0b111 }:
                    rv = new BGEU();
                    break;

                #endregion

                #region S_Type 3个指令
                case { OpCode: OpCodeEnum.STORE, Fun3: 0b000 }:
                    rv = new SB();
                    break;
                case { OpCode: OpCodeEnum.STORE, Fun3: 0b001 }:
                    rv = new SH();
                    break;
                case { OpCode: OpCodeEnum.STORE, Fun3: 0b010 }:
                    rv = new SW();
                    break;

                #endregion

                #region U_Type 2个指令
                case { OpCode: OpCodeEnum.AUPIC}:
                    rv = new AUIPC();
                    break;
                case { OpCode: OpCodeEnum.LUI}:
                    rv = new LUI();
                    break;
                #endregion

                #region J_Type 1个指令
                case { OpCode: OpCodeEnum.JAL}:
                    rv = new JAL();
                    break;
                #endregion

                default:
                    return ins;
            }
            rv.OpCode = ins.OpCode;
            rv.Rd  = ins.Rd;
            rv.Rs1 = ins.Rs1;
            rv.Rs2 = ins.Rs2;
            rv.InsType = ins.InsType;
            rv.Fun3 = ins.Fun3;
            rv.Fun7 = ins.Fun7;
            rv.Immediate = ins.Immediate;

            return rv;
        }

        public virtual void Execute(Cpu cpuinfo)
        {
            return;
        }

        public override string ToString()
        {
            string rv = "";
            string cmd = this.GetType().Name.ToLower();
            if(cmd == "instruction")
            {
                return ascii;
            }
            switch (this.InsType)
            {
                case InstructionTypeEnum.R:
                    rv = $"{cmd} x{Rd},x{Rs1},x{Rs2}"; 
                    break;
                case InstructionTypeEnum.I:
                    if (cmd.StartsWith("l") || cmd == "jalr")
                    {
                        rv = $"{cmd} x{Rd},{Immediate.ToString()}(x{Rs1})";
                    }
                    else
                    {
                        rv = $"{cmd} x{Rd},x{Rs1},{Immediate.ToString()}";
                    }
                    break;
                case InstructionTypeEnum.S:
                    rv = $"{cmd} x{Rs2},{Immediate.ToString()}(x{Rs1})";
                    break;
                case InstructionTypeEnum.B:
                    rv = $"{cmd} x{Rs1},x{Rs2},{(Immediate/2).ToString()}";
                    break;
                case InstructionTypeEnum.U:
                    rv = $"{cmd} x{Rd},{Immediate.ToString()}";
                    break;
                case InstructionTypeEnum.J:
                    rv = $"{cmd.ToLower()} x{Rd},{(Immediate/2).ToString("")}";
                    break;
                default:
                    break;
            }
            return rv;
        }

    }
}
